package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/joho/godotenv"
	"gocv.io/x/gocv"
)

var (
	latestFrames []gocv.Mat
	frameLocks   []sync.RWMutex
	streamURLs   []string
)

func captureStream(streamURL string, frameIndex int) {
	webcam, err := gocv.OpenVideoCapture(streamURL)
	if err != nil {
		log.Printf("无法打开视频流 %s: %v\n", streamURL, err)
		return
	}
	defer webcam.Close()

	img := gocv.NewMat()
	defer img.Close()
	for {
		if ok := webcam.Read(&img); !ok {
			log.Printf("无法读取帧 %s，重新连接...\n", streamURL)
			time.Sleep(10 * time.Millisecond)
			webcam, err = gocv.OpenVideoCapture(streamURL)
			if err != nil {
				log.Printf("重新连接 %s 失败: %v\n", streamURL, err)
				continue
			}
			continue
		}
		if img.Empty() {
			continue
		}
		frameLocks[frameIndex].Lock()
		if latestFrames[frameIndex].Empty() {
			latestFrames[frameIndex] = gocv.NewMat()
		}
		img.CopyTo(&latestFrames[frameIndex])
		frameLocks[frameIndex].Unlock()
		// time.Sleep(30 * time.Millisecond)
	}
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	cameraIndex := 0
	path := r.URL.Path
	if path != "/" && len(path) > 1 {
		indexStr := path[1:]
		if idx, err := strconv.Atoi(indexStr); err == nil && idx > 0 && idx <= len(streamURLs) {
			cameraIndex = idx - 1
		} else {
			http.NotFound(w, r)
			return
		}
	}

	cameraNumber := cameraIndex + 1
	html := fmt.Sprintf(`<!DOCTYPE html>
<html style="height: 100%%;">
<head>
    <meta name="viewport" content="width=device-width, minimum-scale=0.1">
    <title>摄像头%d</title>
    <style>
        body {
            margin: 0px; 
            height: 100%%; 
            background-color: rgb(14, 14, 14);
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
        }
        .camera-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            width: 100%%;
            height: 100%%;
        }
        .camera-feed {
            width: 90%%;
            max-width: 800px;
        }
        img {
            display: block;
            -webkit-user-select: none;
            margin: auto;
            background-color: hsl(0, 0%%, 25%%);
            width: 100%%;
            height: auto;
        }
        h2 {
            color: white;
            text-align: center;
            font-family: Arial, sans-serif;
        }
    </style>
</head>
<body>
    <div class="camera-container">
        <div class="camera-feed">
            <h2>摄像头%d</h2>
            <img src="/video/%d" alt="视频流%d">
        </div>
    </div>
</body>
</html>`, cameraNumber, cameraNumber, cameraNumber, cameraNumber)

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(html))
}

func videoHandler(w http.ResponseWriter, r *http.Request) {
	cameraIndex := 0
	path := r.URL.Path
	if len(path) > 7 {
		indexStr := path[7:]
		if idx, err := strconv.Atoi(indexStr); err == nil && idx > 0 && idx <= len(streamURLs) {
			cameraIndex = idx - 1
		}
	}

	w.Header().Set("Content-Type", "multipart/x-mixed-replace; boundary=frame")
	w.Header().Set("Connection", "close")
	frameChan := make(chan []byte, 5)
	go func() {
		defer close(frameChan)
		ticker := time.NewTicker(10 * time.Millisecond)
		defer ticker.Stop()
		for {
			select {
			case <-r.Context().Done():
				return
			case <-ticker.C:
				frameLocks[cameraIndex].RLock()
				if !latestFrames[cameraIndex].Empty() {
					buf, err := gocv.IMEncode(".jpg", latestFrames[cameraIndex])
					if err == nil {
						frameChan <- buf.GetBytes()
					}
				}
				frameLocks[cameraIndex].RUnlock()
			}
		}
	}()

	for frameData := range frameChan {
		_, err := w.Write([]byte("--frame\r\n"))
		if err != nil {
			return
		}
		_, err = w.Write([]byte("Content-Type: image/jpeg\r\n\r\n"))
		if err != nil {
			return
		}
		_, err = w.Write(frameData)
		if err != nil {
			return
		}
		_, err = w.Write([]byte("\r\n\r\n"))
		if err != nil {
			return
		}
		if flusher, ok := w.(http.Flusher); ok {
			flusher.Flush()
		}
	}
}

func main() {
	err := godotenv.Load()
	if err != nil {
		log.Println("未找到.env文件, 将使用默认环境变量")
	}

	streamURLs = make([]string, 0)
	for i := 1; ; i++ {
		key := fmt.Sprintf("STREAM_URL_%d", i)
		url := os.Getenv(key)
		if url == "" {
			break
		}
		streamURLs = append(streamURLs, url)
	}

	if len(streamURLs) == 0 {
		if url := os.Getenv("STREAM_URL"); url != "" {
			streamURLs = append(streamURLs, url)
		}
	}

	cameraCount := len(streamURLs)
	latestFrames = make([]gocv.Mat, cameraCount)
	frameLocks = make([]sync.RWMutex, cameraCount)

	for i, url := range streamURLs {
		latestFrames[i] = gocv.NewMat()
		go captureStream(url, i)
	}

	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/{id}", indexHandler)
	http.HandleFunc("/video/", videoHandler)
	port := os.Getenv("PORT")
	if port == "" {
		port = "5000"
	}
	addr := fmt.Sprintf(":%s", port)
	log.Printf("服务已启动，请访问 http://localhost:%s\n", port)
	log.Fatal(http.ListenAndServe(addr, nil))
}
